package com.xxm.mediacodec

import android.app.Application
import android.content.res.AssetFileDescriptor
import android.graphics.Bitmap
import android.graphics.Paint
import android.graphics.Rect
import android.media.*
import android.util.Log
import android.view.SurfaceHolder
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.xxm.util.AudioPlayer
import kotlinx.coroutines.*
import java.io.File
import java.text.DecimalFormat


class MediaCodecModel(application: Application) : AndroidViewModel(application) {
    private val TAG = "MediaCodecModel"
    fun supportCodecType() {
        val mediaCodecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
        mediaCodecList.codecInfos.forEach {
            if (it.isEncoder) {
                Log.d(TAG, "编码器：${it.name}")
            } else {
                Log.d(TAG, "解码器：${it.name}")
            }
        }

    }


    private lateinit var mediaExtractor: MediaExtractor
    private lateinit var mediaCodec: MediaCodec

    private var inputIndex = 0

    private var outIndex = 0
    private var audioOutputIndex = 0

    private var job: Job? = null

    private var isPlaying = false

    val playState = MutableLiveData(0)
    val videoInfo = MutableLiveData(VideoInfo(0, 0, 0))

    private val audioExtractor: MediaExtractor = MediaExtractor()
    private var audioDecoder: MediaCodec? = null
    private var audioTrack: AudioTrack? = null
    private var audioJob: Job? = null
    fun startAudio(mp4: String) {

        audioJob = viewModelScope.launch(Dispatchers.IO) {
            audioExtractor.setDataSource(mp4)
            var audioChannel = 0
            var audioSample = 0
            var mineType = ""
            var mediaFormat: MediaFormat? = null
            for (i in 0 until audioExtractor.trackCount) {
                mediaFormat = audioExtractor.getTrackFormat(i)
                mineType = mediaFormat.getString(MediaFormat.KEY_MIME) ?: ""
                if (mineType?.startsWith("audio/") == true) {
                    audioExtractor.selectTrack(i)
                    audioSample = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)
                    audioChannel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
                    Log.d(TAG, ">>> audio sample:${audioSample},channel:${audioChannel}")
                    break
                }
            }

            val minBufferSize = AudioTrack.getMinBufferSize(audioSample, if (audioChannel == 1) AudioFormat.CHANNEL_OUT_MONO else AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT)
            Log.d(TAG, "audio track min buffer size:${minBufferSize}")
            audioTrack = AudioTrack(AudioManager.STREAM_MUSIC, audioSample, if (audioChannel == 1) AudioFormat.CHANNEL_OUT_MONO else AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM)
            audioDecoder = MediaCodec.createDecoderByType(mineType)
            audioDecoder?.configure(mediaFormat, null, null, 0)
            audioDecoder?.start()
            audioTrack?.play()
            val bufferInfo = MediaCodec.BufferInfo()
            var lastTime = System.currentTimeMillis()
            while (isActive) {
                val inputIndex = audioDecoder!!.dequeueInputBuffer(10000)
                if (inputIndex > 0) {
                    val inputBuffer = audioDecoder!!.getInputBuffer(inputIndex)
                    val sampleSize = audioExtractor.readSampleData(inputBuffer!!, 0)
                    Log.d(TAG, ">>>> sample size:${sampleSize}")
                    if (sampleSize < 0) {
                        Log.d(TAG, ">>>> input buffer is EOS")
                        audioDecoder!!.queueInputBuffer(
                            inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                        )
                    } else {
                        Log.d(TAG, ">>>>audio input buffer")
                        audioDecoder!!.queueInputBuffer(
                            inputIndex, 0, sampleSize, audioExtractor.sampleTime, 0
                        )
                        audioExtractor.advance()
                    }
                }

                var outputIndex = audioDecoder!!.dequeueOutputBuffer(bufferInfo, 10000)
                Log.d(TAG, ">>> audio index:${outputIndex}")
                if (outputIndex >= 0) {
                    val outputBuffer = audioDecoder!!.getOutputBuffer(outputIndex)
                    val chunPCM = ByteArray(bufferInfo.size)
                    outputBuffer?.get(chunPCM)

                    audioTrack!!.write(chunPCM, 0, bufferInfo.size)
                    audioDecoder?.releaseOutputBuffer(outputIndex, false)
                }


            }
            audioTrack?.stop()
            audioDecoder?.release()
            audioTrack?.release()
        }


    }

    private var audioPlayer = AudioPlayer()

    fun getVideoInfo(path: String, isAssert: Boolean) {
        val mediaExtractor = MediaExtractor()

        val info = VideoInfo()
        if (isAssert) {
            val afd: AssetFileDescriptor = getApplication<Application>().assets.openFd(path)
            mediaExtractor.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length);
            info.fileSize = afd.length
            info.name=path
        } else {
            mediaExtractor.setDataSource(path)
            info.fileSize = File(path).length()
            info.name=path
        }
        val numTracks: Int = mediaExtractor.getTrackCount()


        for (i in 0 until numTracks) {
            val format: MediaFormat = mediaExtractor.getTrackFormat(i)
            val mime = format.getString(MediaFormat.KEY_MIME)
            if (mime!!.startsWith("audio/")) {
                // 处理音频轨道
            } else if (mime!!.startsWith("video/")) {
                info.width = format.getInteger(MediaFormat.KEY_WIDTH)
                info.height = format.getInteger(MediaFormat.KEY_HEIGHT)
                info.bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE)
            }
        }
        videoInfo.value = info
    }

    fun start(mp4: String, holder: SurfaceHolder, isAssert: Boolean = false) {
        if (isPlaying) return
        isPlaying = true
        //  startAudio(mp4)
        audioPlayer.playMp4(mp4)
        job = viewModelScope.launch(Dispatchers.IO) {
            mediaExtractor = MediaExtractor()
            if (isAssert) {
                val afd: AssetFileDescriptor = getApplication<Application>().getAssets().openFd(mp4)
                mediaExtractor.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            } else {
                mediaExtractor.setDataSource(mp4)
            }

            for (i in 0 until mediaExtractor.trackCount) {
                val mediaFormat = mediaExtractor.getTrackFormat(i)
                Log.d(TAG, ">>>>format,index:${i},format:${mediaFormat}")
                val mineType = mediaFormat.getString(MediaFormat.KEY_MIME)
                Log.d(TAG, ">>>>mine,index:${i},mine:${mineType}")
                if (mineType?.startsWith("video/") == true) {
                    Log.d(TAG, ">>>> select:${mineType},${mediaFormat}")
                    mediaExtractor.selectTrack(i)
                    mediaCodec = MediaCodec.createDecoderByType(mineType)
                    mediaCodec.configure(mediaFormat, holder.surface, null, 0)
                    break
                }
            }

            mediaCodec.start()
            val bufferInfo = MediaCodec.BufferInfo()
            var isEos = false
            var startMs = System.currentTimeMillis()
            while (isActive) {
                if (!isEos) {
                    inputIndex = mediaCodec.dequeueInputBuffer(1000)
                    if (inputIndex >= 0) {
                        val inputBuffer = mediaCodec.getInputBuffer(inputIndex)
                        val sampleSize = mediaExtractor.readSampleData(inputBuffer!!, 0)
                        Log.d(TAG, ">>>> sample size:${sampleSize}")
                        if (sampleSize < 0) {
                            Log.d(TAG, ">>>> input buffer is EOS")
                            mediaCodec.queueInputBuffer(
                                inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                            )
                            isEos = true
                        } else {
                            mediaCodec.queueInputBuffer(
                                inputIndex, 0, sampleSize, mediaExtractor.sampleTime, 0
                            )
                            mediaExtractor.advance()
                        }
                    }
                }

                var outputIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
                when (outputIndex) {
                    MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> Log.d(TAG, ">>>>INFO_OUTPUT_FORMAT_CHANGED")
                    MediaCodec.INFO_TRY_AGAIN_LATER -> {
                        Log.d(TAG, ">>>>INFO_TRY_AGAIN_LATER")
                    }
                    MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> {
                        // val outputBuffer = mediaCodec.getOutputBuffer(outputIndex)
                        Log.d(TAG, ">>>>INFO_OUTPUT_BUFFERS_CHANGED")
                    }
                    else -> {
                        while (bufferInfo.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                            Thread.sleep(10)
                            Log.d(TAG, "release index:${outputIndex}")
                            try {
                                if (outputIndex != outIndex) {
                                    outIndex = outputIndex
                                    mediaCodec.releaseOutputBuffer(outputIndex, true)
                                }
                            } catch (e: java.lang.Exception) {
                                e.printStackTrace()
                            }
                        }
                    }
                }

                if (bufferInfo.flags.and(MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.d(TAG, ">>>>>>>>>>BUFFER_FLAG_END_OF_STREAM")
                    break
                }
            }
            audioPlayer.release()
            mediaCodec.stop()
            mediaCodec.release()
            mediaExtractor.release()
            isPlaying = false
            withContext(Dispatchers.Main) {
                playState.value = 1
            }
            cancel()
        }

    }

    fun stop() {
        job?.cancel()
        job = null
        audioJob?.cancel()
        audioJob = null
    }

    fun formatFileSize(fileS: Long): String? {
        val df = DecimalFormat("#.00")
        var fileSizeString = ""
        val wrongSize = "0B"
        if (fileS == 0L) {
            return wrongSize
        }
        fileSizeString = if (fileS < 1024) {
            df.format(fileS.toDouble()) + "B"
        } else if (fileS < 1024 * 1024) {
            df.format(fileS.toDouble() / 1024) + "KB"
        } else if (fileS < 1024 * 1024 * 1024) {
            df.format(fileS.toDouble() / (1024 * 1024)) + "MB"
        } else {
            df.format(fileS.toDouble() / (1024 * 1024 * 1024)) + "GB"
        }
        return fileSizeString
    }

    fun setThumbnail(holder: SurfaceHolder, bitmap: Bitmap, width: Int, height: Int) {
        val paint = Paint()
        val canvas = holder.lockCanvas()
        Log.d(TAG, "bitmap width:${bitmap.width},height:${bitmap.height}")
        Log.d(TAG, "view width:${width},height:${height}")
        canvas?.drawBitmap(
            bitmap!!, Rect(0, 0, bitmap.width, bitmap.height),
            Rect(0, 0, width, height), paint
        )
        holder.unlockCanvasAndPost(canvas)
    }


//    fun createMediaCodec(){
//        val mediaCodec= MediaCodec .createByCodecName(Medi)
//    }
}